#ifndef _OM_STRING_H_
#define _OM_STRING_H_

#include "OmPrerequisite.h"

namespace OmPublic
{	

#ifdef OM_UNICODE
#ifndef _T
#define _T(Str) L##Str
#endif
#define _tcscpy wcscpy
#define _tcscmp wcscmp
#else
#ifndef _T
#define _T(Str) Str
#endif
#define _tcscpy strcpy
#define _tcscmp strcmp
#endif

	template <typename CharType, typename StringType>
	class /*OmExport*/ StringT
	{
	public:
		StringT(){}
		StringT(const char* str);
		StringT(const wchar_t* str);
//		StringT(const CharType* str){mStr = str;}
		StringT(const StringType & str){mStr = str;}
		
		size_t length() const {return mStr.length();}
		const CharType* c_str() const {return mStr.c_str();}
		int compare(const StringT & str, bool caseSensitive = true) const 
		{
			if (caseSensitive)
				return mStr.compare(str.mStr);
			else
			{
				StringType tmp1 = mStr;
				StringType tmp2 = str.mStr;
				std::transform(
					tmp1.begin(),
					tmp1.end(),
					tmp1.begin(),
					tolower);
				std::transform(
					tmp2.begin(),
					tmp2.end(),
					tmp2.begin(),
					tolower);

				return tmp1.compare(tmp2);
			}
		}
		bool operator == (const StringT & str) const 
		{
			return compare(str, true) == 0;
		}
		bool operator != (const StringT & str) const 
		{
			return ! (operator == (str));
		}

		void toLowerCase()
		{
			std::transform(
				mStr.begin(),
				mStr.end(),
				mStr.begin(),
				tolower);
		}
		void trimLeft();
		void trimRight();
		void trim()
		{
			trimRight();
			trimLeft();
		}
		void toUpperCase()
		{
			std::transform(mStr.begin(), mStr.end(), mStr.begin(), toupper);
		}
		void split(vector<StringType> & v, const StringType & delimitation) const
		{			
			for (size_t start = 0; ;)
			{
				StringType::size_type pos = mStr.find_first_of(delimitation, start);
				if (pos == StringType::npos)
				{
					v.push_back(mStr.substr(start));
					break;
				}
				else
				{
					v.push_back(mStr.substr(start, pos - start ));
					start = pos + 1;
					if (start == mStr.length())
						break;
				}
			}

		}
		bool match (const StringT & str, bool caseSensitive = true) const
		{
			if (length() != str.length())
				return false;

			size_t len = length();				
			const CharType * src = c_str(), * dst = str.c_str();
			CharType tmp1, tmp2;
			for (size_t i = 0; i < len ; ++i)
			{
				tmp1 = src[i];
				tmp2 = dst[i];
				if (!caseSensitive)
				{
					tmp1 = tolower(tmp1);
					tmp2 = tolower(tmp2);
				}
				if (tmp1 != tmp2)
					return false;
			}
			return true;

		}

		CharType getAt(size_t pos) const
		{
 			if (pos >= length())
 				return 0;
			return mStr[pos];
		}

		void splitFileName(StringT & file, StringT & path, bool endWithSlash = false) const;

		const CharType & at(size_t pos) const {return mStr.at(pos);}
		bool empty() const {return mStr.empty();}
		void clear(){mStr.clear();}
		void erase(size_t pos){if (pos >= 0 && pos < length()) mStr.erase(pos);}
		void  format( const CharType * formatStr, ...);
		const StringT & operator += (const CharType * str)
		{
			mStr += str;
			return (*this);
		}
		const StringT & operator += (CharType c)
		{
			mStr += c;
			return (*this);
		}
		size_t find(const StringT & str) const
		{
			return mStr.find(str.mStr);
		}		
		size_t find(const StringT & str, size_t begin) const
		{
			return mStr.find(str.mStr, begin);
		}
// 		size_t find(const StringType & str) const 
// 		{
// 			return mStr.find(str);
// 		}
// 		size_t find (const StringType & str, size_t begin) const
// 		{
// 			return mStr.find(str, begin);
// 		}
		void replace(size_t pos, CharType c)
		{
			if (pos >= length())
				return;
			mStr[pos] = c;
		}
		void replaceOnce(const StringType & pattern, const StringType & newpat)
		{
			replace(pattern, newpat, 1);
		}

		size_t replace(const StringType & pattern, const StringType & newpat, size_t times)
		{
			size_t count = 0;
			const size_t nsize = newpat.length();
			const size_t psize = pattern.length();

			for(size_t pos = mStr.find(pattern/*.mStr*/, 0); 
				pos != std::string::npos;
				pos = mStr.find(pattern/*.mStr*/, pos + nsize))
			{
				mStr.replace(pos, psize, newpat/*.mStr*/);
				count++;

				if (count == times)
					break;
			}

			return count;
		}
		
		size_t replace_all( const StringType & pattern,  const StringType & newpat)
		{
			size_t count = 0;
			const size_t nsize = newpat.length();
			const size_t psize = pattern.length();

			for(size_t pos = mStr.find(pattern/*.mStr*/, 0); 
				pos != std::string::npos;
				pos = mStr.find(pattern/*.mStr*/, pos + nsize))
			{
				mStr.replace(pos, psize, newpat/*.mStr*/);
				count++;
			}

			return count;
		} 
		size_t occur( const StringType & pattern)
		{
			size_t count = 0;
			const size_t psize = pattern.GetAllocLength();

			for (size_t pos = mStr.find(pattern, 0);
				pos != std::string::npos;
				pos = mStr.find(pattern, pos + psize))
			{
				++count;
			}

			return count;
		}

		StringT & assign(const CharType * str, int len)
		{
			mStr.assign(str, len);
			return (*this);
		}

		const StringT& append(const CharType* str, size_t count)
		{
			mStr.append(str, count);
			return (*this);
		}
		bool operator < (const StringT & str) const
		{
			return mStr < str.mStr;
		}
		const StringT & operator += (const StringT & str)
		{
			mStr += str.c_str();
			return (*this);
		}
		operator const StringType & (){return mStr;}
		operator const CharType * (){return (const CharType*)mStr.c_str();}
		const StringT & operator = (const StringT & str)
		{
			mStr = (const StringType&) str;
			return (*this);
		}
		const StringT & operator = (const char*);

	private:
		StringType mStr;
	};

	typedef StringT<char, std::string> StringA;
	typedef StringT<wchar_t, std::wstring> StringW;

#ifdef OM_UNICODE
	typedef StringW String;
#else
	typedef StringA String;
#endif
	



}
#endif